precision highp float;

varying vec2 var_uv;
uniform sampler2D tex;
uniform float time;
uniform vec2 pixel_size;
uniform int algorithm;
uniform int num_samples;
uniform float radius;
const int MAX_NUM_SAMPLES = 256;

// uniform over [0, 1]
float rand(vec2 co)
{
    return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
}

// uniform over [a, b], where a=0 and b=1
//     mean = (b-a)    /  2 = 1 /  2
// variance = (b-a)**2 / 12 = 1 / 12
float uniform_dist(vec2 co, int n)
{
    return rand(co + 0.07 * time + 0.11 * float(n));
}

//     mean = mean(uniform_dist)         = 1 /  2
// variance = variance(uniform_dist) / 4 = 1 / 48
float normal_dist(vec2 co, int n)
{
    float r0 = rand(co + 0.07 * time + 0.19*float(n));
    float r1 = rand(co + 0.11 * time + 0.23*float(n));
    float r2 = rand(co + 0.13 * time + 0.29*float(n));
    float r3 = rand(co + 0.17 * time + 0.31*float(n));
    return (r0 + r1 + r2 + r3) / 4.0;
}

//     mean = 0
// variance = 1
float ordinary_normal_dist(vec2 co, int n)
{
    // sqrt(48.0) == sqrt(3.0) * 4.0
    return (normal_dist(co, n) - 0.5) * sqrt(3.0) * 4.0;
}

void main()
{
    vec4 color = vec4(0);

    for (int i=0; i<MAX_NUM_SAMPLES; i++) {
        if (i == num_samples) break;

        int i2 = i * 2;
        vec2 bias;
        if (algorithm == 0) {
            bias.x = (uniform_dist(var_uv, i2    ) - 0.5) * radius;
            bias.y = (uniform_dist(var_uv, i2 + 1) - 0.5) * radius;
        }
        else if (algorithm == 1) {
            bias.x = (normal_dist(var_uv, i2    ) - 0.5) * radius;
            bias.y = (normal_dist(var_uv, i2 + 1) - 0.5) * radius;
        }
        else if (algorithm == 2) {
            bias.x = ordinary_normal_dist(var_uv, i2    ) * sqrt(radius);
            bias.y = ordinary_normal_dist(var_uv, i2 + 1) * sqrt(radius);
        }
        else {
            color = vec4(1, 0, 0, 1);
            break;
        }
        color += texture2D(tex, var_uv + bias * pixel_size);
    }

    gl_FragColor = color / float(num_samples);
}

